package com.biao.threadPool;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingDeque;

/**
 * @description: 手写线程池，添加关闭线程池的功能，不要小看这个关闭功能，需要考虑的点比较多：
 *                  1. 关闭前不能再往队列里面放任务
 *                  2. 执行任务的线程要根据结束标识来结束，但要保证队列里面的任务执行完成
 *                  3. 对于被阻塞的线程要中断，让它们起来，把东西执行完
 * @author:chenwenbiao
 * @createTime:2020/1/17 9:33
 * @Version：1.0
 **/
public class MyFixedSizeThreadPoolWithShutDown {

    /**
     * 1. 线程池的构成：队列 线程
      */
    private BlockingQueue<Runnable> queue;

    /**
     * 2. 线程需要管理--容器
     */
    private List<Thread> workers;


    /**
     * work就是一个干活的人，一直等着将代码块运到cpu上执行，代码块就是任务，外面调用时将任务添加进
     */
    private static class Worker extends Thread{
        private MyFixedSizeThreadPoolWithShutDown pool;

        public Worker(MyFixedSizeThreadPoolWithShutDown pool){
            this.pool = pool;
        }

        /**
         * 每个线程具体的工作
         */
        @Override
        public void run() {
            // 做什么？ ---执行代码--runnable的run方法代码块
            Runnable task = null;

            /**
             * 如果池还有工作没做完，得继续完成才能关闭，得加上条件：pool.queue.size() > 0
             */
            while (pool.isWorking || pool.queue.size() > 0){
                try {
                    if(pool.isWorking) {
                        // 死等到有任务才往下执行
                        task = pool.queue.take();
                    }
                    else{
                        // 要关闭的时候，就直接弹出队列的任务出来执行，而不是使用take会阻塞的等到有任务为止，poll没有数据就直接返回
                        task = pool.queue.poll();
                    }
                } catch (InterruptedException e){
                    e.printStackTrace();
                }

                if(task != null){
                    task.run();
                    System.out.println("线程：" + Thread.currentThread().getName() + "，执行完毕");
                }
            }
        }
    }


    /**
     * 线程池初始化的构造方法
     * @param taskSize
     * @param poolSize
     */
    public MyFixedSizeThreadPoolWithShutDown(int taskSize, int poolSize){
        if(taskSize <= 0 || poolSize <= 0){
            throw new IllegalArgumentException("非法参数！");
        }

        this.queue = new LinkedBlockingDeque<>(taskSize);
        this.workers = Collections.synchronizedList(new ArrayList<>(poolSize));
        for(int i = 0 ; i < poolSize ; i++){
            Worker worker = new Worker(this);
            worker.start();
            workers.add(worker);
        }
    }


    /**
     * 将任务放入仓库
     * @param task
     * @return
     */
    public boolean submit(Runnable task){
        if(isWorking){
            // 如果线程池还在工作，就可以添加代码块进来处理
            return this.queue.offer(task);
        }
        else{
            // 线程池要下班，不干活，就不能再添加代码块过来
            return false;
        }
    }

    /**
     * 关闭线程池方法
     */
    private volatile boolean isWorking = true;
    public void shutDown(){
        this.isWorking = false;

        /**
         * 已经被阻塞的线程怎么办？ -----中断它----- interrupt
         */
        for(Thread thread: workers){
            if(thread.getState().equals(Thread.State.BLOCKED)){
                // 中断线程
                thread.interrupt();
            }
        }

    }

}
